package org.japo.java.jdbc.samples;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author José A. Pacheco Ondoño - joanpaon@gmail.com
 */
public class AccesoAlumnos {

    // Nombre de la base de datos
    private final String BD = "Agenda";

    // SGBD soportador - Selección del SGBD activo
    private final String PRODUCTO1 = "Access";   // Access
    private final String PRODUCTO2 = "MySQL";    // MySQL
    private final String PRODUCTO_ACTIVO = PRODUCTO2;

    // Referencias para la gestión de la base de datos
    private Connection con = null;
    private Statement stmt = null;

    public static void main(String[] args) {
        try {
            // Crea una instancia de la propia clase
            AccesoAlumnos aplicacion = new AccesoAlumnos();

            // Ejecuta el aplicativo
            aplicacion.lanzarAplicacion();
        } catch (Exception e) {
            Logger.getLogger(AccesoAlumnos.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    private void lanzarAplicacion() throws Exception {
        try {
            // Crear el acceso a la base de datos
            crearAccesoDatos(BD);

            // Procesa los datos
            procesarDatos();
        } finally {
            // Cerrar la base de datos
            cerrarBD();
        }
    }

    private void crearAccesoDatos(String bd) {
        try {
            // Establecer conexión con la base de datos
            con = conectarBD(bd);

            // ---- CAMBIOS DE DATOS ----
            // ResultSet.TYPE_FORWARD_ONLY (*) - Indica que el objeto ResultSet se 
            //      puede recorrer unicamente hacia adelante.
            // ResultSet.TYPE_SCROLL_INSENSITIVE - Indica que el objeto ResultSet se 
            //      puede recorrer, pero en general no es sensible a los cambios en 
            //      los datos que subyacen en él.
            // ResultSet.TYPE_SCROLL_SENSITIVE - Indica que el objeto ResultSet se 
            //      puede recorrer, y además, los cambios en él repercuten
            //      en la base de datos subyacente.
            // ---- MODOS DE CONCURRENCIA ----
            // ResultSet.CONCUR_READ_ONLY (*) - Indica que en el modo de concurrencia 
            //      para el objeto ResultSet éste no puede ser actualizado.
            // ResultSet.CONCUR_UPDATABLE - Indica que en el modo de concurrencia 
            //      para el objeto ResultSet éste podria ser actualizado.
            //
            // Crear una sentencia
            stmt = con.createStatement();
        } catch (Exception e) {
            // Información de depuración
            Logger.getLogger(AccesoAlumnos.class.getName()).log(Level.SEVERE, null, e);

            // Cierra la base de datos
            cerrarBD();

            // Finaliza el programa
            terminarPrograma();
        }
    }

    private Connection conectarBD(String bd) throws Exception {
        // Referencia a la conexión
        Connection c;

        // Conecta con la base de datos
        if (PRODUCTO_ACTIVO.equals(PRODUCTO1)) {
            c = conectarAccess(bd);
        } else if (PRODUCTO_ACTIVO.equals(PRODUCTO2)) {
            c = conectarMySQL(bd);
        } else {
            throw new Exception("No se ha seleccionado producto");
        }

        // Devuelve la conexión obtenida
        return c;
    }

    private Connection conectarAccess(String bd) throws Exception {
        // Conexión
        Connection c;

        // Protocolo del driver
        final String PROTOCOLO_ACCESS = "jdbc:odbc";

        // Cadena de conexión de Access
        final String CADENA_ACCESS = PROTOCOLO_ACCESS + ":" + bd;

        // Realiza la conexión
        System.out.println("Abriendo " + bd + " ...");
        c = DriverManager.getConnection(CADENA_ACCESS);
        System.out.println("Base de datos " + bd + " conectada.");

        // Devolver la conexión obtenida
        return c;
    }

    private Connection conectarMySQL(String bd) throws Exception {
        // Conexión
        Connection c;

        // Protocolo del driver
        final String PROTOCOLO_MySQL = "jdbc:mysql";

        // Servidor
        final String SERVIDOR = "localhost";
        final int PUERTO = 3306;

        // Usuario
        final String USUARIO = "usuario";
        final String PASSWORD = "usuario";

        // Cadena de conexión de MySQL
        final String CADENA_MySQL = ""
                + PROTOCOLO_MySQL + "://"
                + SERVIDOR + ":"
                + PUERTO + "/"
                + bd;

        // Realiza la conexión
        System.out.println("Abriendo " + bd + " ...");
        c = DriverManager.getConnection(CADENA_MySQL, USUARIO, PASSWORD);
        System.out.println("Base de datos " + bd + " conectada.");

        // Devolver la conexión obtenida
        return c;
    }

    private void procesarDatos() throws Exception {
        // Eliminar todos los alumnos de la tabla
        vaciarDatos();

        // Insertar datos arbitrarios de alumnos 
        insertarDatos();

        // Listar alumnos
        mostrarDatos();

        // Modificar registros
        modificarDatos();

        // Listar alumnos
        mostrarDatos();

        // Borrar registros
        borrarDatos();

        // Listar alumnos
        mostrarDatos();
    }

    private void mostrarDatos() throws Exception {
        // Preparación del acceso
        final String SQL = "SELECT * FROM Alumno";

        // Lista los datos
        try (ResultSet rs = stmt.executeQuery(SQL)) {
            // Lista los datos
            listarDatos(rs);
        }
    }

    private void insertarDatos() throws Exception {
        // Sentencia SQL de inserción de datos arbitrarios
        final String SQL1 = "INSERT INTO Alumno "
                + "(exp, nombre, apellidos, nif, nac, telefono, email) "
                + "VALUES (71, 'Freddy', 'Mercury', '11111111-F', '2011-05-25', "
                + "999111111, 'freddy@domain.org')";

        // Sentencia SQL de inserción de datos arbitrarios
        final String SQL2 = "INSERT INTO Alumno "
                + "(exp, nombre, apellidos, nif, nac, telefono, email) "
                + "VALUES (72, 'Montserrat', 'Caballé', '22222222-M', '2013-02-12', "
                + "999222222, 'montse@domain.org')";

        // Sentencia SQL de inserción de datos arbitrarios
        final String SQL3 = "INSERT INTO Alumno "
                + "(exp, nombre, apellidos, nif, nac, telefono, email) "
                + "VALUES (73, 'Bart', 'Simpson', '99999999-X', '2013-03-13', "
                + "987654321, 'bart@dividebyzero.org')";

        // Ejecución de las sentencias SQL por lotes
        stmt.addBatch(SQL1);
        stmt.addBatch(SQL2);
        stmt.addBatch(SQL3);
        int[] filas = stmt.executeBatch();

        // Mensaje de valoración final
        System.out.println(filas.length + " fila/s insertada/s.");
    }

    private void modificarDatos() throws Exception {
        final String SQL = "UPDATE Alumno SET nif='98765432-K' WHERE nombre='Montserrat'";
        int filas = stmt.executeUpdate(SQL);
        System.out.println(filas + " fila/s actualizada/s.");
    }

    private void borrarDatos() throws Exception {
        final String SQL = "DELETE FROM Alumno WHERE nombre='Bart'";
        int filas = stmt.executeUpdate(SQL);
        System.out.println(filas + " fila/s borrada/s.");
    }

    private void vaciarDatos() throws Exception {
        final String SQL = "DELETE FROM Alumno";
        int filas = stmt.executeUpdate(SQL);
        System.out.println(filas + " fila/s borrada/s.");
    }

    private void listarDatos(ResultSet rs) throws Exception {
        // Mapeado de campos
        int numExp;
        String nombre;
        String apellidos;
        String nif;
        String fechaNac;
        String telefono;
        String email;

        // Cabecera del informe
        System.out.println("Tabla: Alumnos");
        System.out.println("----------------------------");

        // Generación del informe - Fila a fila
        while (rs.next()) {
            // Las columnas del ResultSet enpiezan en 1
            // Se pueden referir por su nombre o su posición
            numExp = rs.getInt(1);
            nombre = rs.getString(2);
            apellidos = rs.getString(3);
            nif = rs.getString(4);
            fechaNac = rs.getDate(5).toString();   // Convertir a String
            telefono = rs.getString(6);
            email = rs.getString(7);

            // Genera una linea de texto formateada
            String linea = String.format(
                    "%3d - %-3s - %-20s - %-20s - %10s - %10s - %9s - %s",
                    rs.getRow(), numExp, nombre, apellidos, nif, fechaNac, telefono, email);

            // Muestra la linea
            System.out.println(linea);
        }
    }

    private void cerrarBD() {
        // Mensaje
        System.out.println("Cerrando " + BD + " ...");

        // Cierra el Statement
        try {
            stmt.close();
        } catch (SQLException | NullPointerException e) {
            Logger.getLogger(AccesoAlumnos.class.getName()).log(Level.SEVERE, null, e);
        }

        // Cierra la conexión
        try {
            con.close();
        } catch (SQLException | NullPointerException e) {
            Logger.getLogger(AccesoAlumnos.class.getName()).log(Level.SEVERE, null, e);
        }

        // Mensaje
        System.out.println("Base de datos " + BD + " cerrada.");
    }

    private void terminarPrograma() {
        System.exit(0);
    }
}
